package com.ruoyi.system.service;

import cn.dev33.satoken.secure.BCrypt;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.PublishConstants;
import com.ruoyi.common.core.domain.dto.RoleDTO;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.*;
import com.ruoyi.common.core.service.LogininforService;
import com.ruoyi.common.core.service.WechatNewService;
import com.ruoyi.common.enums.DeviceType;
import com.ruoyi.common.enums.LoginType;
import com.ruoyi.common.enums.UserStatus;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.exception.user.CaptchaException;
import com.ruoyi.common.exception.user.CaptchaExpireException;
import com.ruoyi.common.exception.user.UserException;
import com.ruoyi.common.helper.LoginHelper;
import com.ruoyi.common.utils.*;
import com.ruoyi.common.utils.redis.RedisUtils;
import com.ruoyi.system.domain.vo.PGroupVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.time.Duration;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 登录校验方法
 *
 * @author Lion Li
 */
@RequiredArgsConstructor
@Slf4j
@Service
public class SysLoginService {

    private final ISysUserService userService;
    private final ISysConfigService configService;
    private final LogininforService asyncService;
    private final SysPermissionService permissionService;
    private final WechatNewService wechatNewService;
    private final IPGroupService iPGroupService;

    /**
     * 请求地址
     **/
    @Value("${wx.webAccessTokenhttps}")
    private String webAccessTokenhttps;
    /**
     * 请求参数
     **/
    @Value("${wx.webAccessTokenParams}")
    private String webAccessTokenParams;
    /**
     * 小程序ID
     **/
    @Value("${wx.appId}")
    private String appId;
    /**
     * 小程序密钥
     **/
    @Value("${wx.secret}")
    private String secret;

    /**
     * 登录验证
     *
     * @param username 用户名
     * @param password 密码
     * @param code     验证码
     * @param uuid     唯一标识
     * @return 结果
     */
    public String login(String username, String password, String code, String uuid) {
        HttpServletRequest request = ServletUtils.getRequest();
        boolean captchaOnOff = configService.selectCaptchaOnOff();
        // 验证码开关
        if (captchaOnOff) {
            validateCaptcha(username, code, uuid, request);
        }
        SysUser user = loadUserByUsername(username);
        checkLogin(LoginType.PASSWORD, username, () -> !BCrypt.checkpw(password, user.getPassword()));
        // 此处可根据登录用户的数据不同 自行创建 loginUser
        LoginUser loginUser = buildLoginUser(user);
        // 生成token
        LoginHelper.loginByDevice(loginUser, DeviceType.PC);
        asyncService.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"), request);
        recordLoginInfo(user.getUserId(), username);
        return StpUtil.getTokenValue();
    }

    /**
     * 登录验证-前端
     *
     * @param username 用户名
     * @param password 密码
     * @param code     验证码
     * @param uuid     唯一标识
     * @return 结果
     */
    public String loginFont(String username, String password, String code, String uuid) {
        HttpServletRequest request = ServletUtils.getRequest();
        boolean captchaOnOff = configService.selectCaptchaOnOff();
        // 验证码开关
        if (captchaOnOff) {
            validateCaptcha(username, code, uuid, request);
        }
        SysUser user = loadUserByUsername(username);
        checkLogin(LoginType.PASSWORD, username, () -> !BCrypt.checkpw(password, user.getPassword()));
        // 此处可根据登录用户的数据不同 自行创建 loginUser
        LoginUser loginUser = buildLoginUser(user);
        // 生成token
        LoginHelper.loginByDevice(loginUser, DeviceType.PC);
        recordLoginInfo(user.getUserId(), username);
        return StpUtil.getTokenValue();
    }

    public String smsLogin(String phonenumber, String smsCode) {
        // 通过手机号查找用户
        SysUser user = loadUserByPhonenumber(phonenumber);

        HttpServletRequest request = ServletUtils.getRequest();
        checkLogin(LoginType.SMS, user.getUserName(), () -> !validateSmsCode(phonenumber, smsCode, request));
        // 此处可根据登录用户的数据不同 自行创建 loginUser
        LoginUser loginUser = buildLoginUser(user);
        // 生成token
        LoginHelper.loginByDevice(loginUser, DeviceType.APP);

        asyncService.recordLogininfor(user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"), request);
        recordLoginInfo(user.getUserId(), user.getUserName());
        return StpUtil.getTokenValue();
    }

    /**
     * 绑定手机号数据校验
     */
    private void checkBindingPhone(WxBindingPhoneRequest request) {
        if (!request.getType().equals("public") && !request.getType().equals("routine") && !request.getType().equals("iosWx") && !request.getType().equals("androidWx")) {
            throw new UserException("未知的用户类型");
        }
            // 参数校验
            if (StrUtil.isBlank(request.getCode())) {
                throw new UserException("小程序获取手机号code不能为空");
            }
            if (StrUtil.isBlank(request.getEncryptedData())) {
                throw new UserException("小程序获取手机号加密数据不能为空");
            }
            if (StrUtil.isBlank(request.getIv())) {
                throw new UserException("小程序获取手机号加密算法的初始向量不能为空");
            }

            WeChatMiniAuthorizeVo response = wechatNewService.miniAuthCode(request.getCode());
//            WeChatMiniAuthorizeVo response = weChatService.programAuthorizeLogin(request.getCode());
            System.out.println("小程序登陆成功 = " + JSON.toJSONString(response));
            String decrypt = WxUtil.decrypt(appId, request.getEncryptedData(), response.getSessionKey(), request.getIv());
            if (StrUtil.isBlank(decrypt)) {
                throw new UserException("微信小程序获取手机号解密失败");
            }
            com.alibaba.fastjson.JSONObject jsonObject = com.alibaba.fastjson.JSONObject.parseObject(decrypt);
            if (StrUtil.isBlank(jsonObject.getString("phoneNumber"))) {
                throw new UserException("微信小程序获取手机号没有有效的手机号");
            }
            request.setPhone(jsonObject.getString("phoneNumber"));
    }


    /**
     * 微信注册绑定手机号
     * @param request 请求参数
     * @return 登录信息
     */
    public LoginResponse registerBindingPhone(WxBindingPhoneRequest request) {
        checkBindingPhone(request);

        // 进入创建用户绑定手机号流程
        Object o = RedisUtils.getCacheObject(request.getKey());
        if (ObjectUtil.isNull(o)) {
            throw new UserException("用户缓存已过期，请清除缓存重新登录");
        }
        RegisterThirdUserRequest registerThirdUserRequest = com.alibaba.fastjson.JSONObject.parseObject(o.toString(), RegisterThirdUserRequest.class);

        SysUser user = userService.selectUserByPhonenumber(request.getPhone());
        if (ObjectUtil.isNull(user)) {
            SysUser registerUser = new SysUser();
            registerUser.setUserName( deWXName( registerThirdUserRequest.getNickName()) + CharUtil.getRandomNum(4));
            registerUser.setWeixinOpenid(registerThirdUserRequest.getOpenId());
            registerUser.setWxAvatar(registerThirdUserRequest.getAvatar());
            registerUser.setPhonenumber(request.getPhone());
            registerUser.setStatus(PublishConstants.COMMON_NO);
            registerUser.setUserType(PublishConstants.USER_TYPE);
            registerUser.setLoginType("register");
            String gender = "2";
            //微信用户表 数据为 性别 0：未知、1：男、2：女
            //系统用户表 数据为 用户性别（0男 1女 2未知）
            if ("0".equals(registerThirdUserRequest.getSex())) {
                gender = "2";
            }
            if ("1".equals(registerThirdUserRequest.getSex())) {
                gender = "0";
            }
            if ("2".equals(registerThirdUserRequest.getSex())) {
                gender = "1";
            }

            registerUser.setSex(gender);
            registerUser.setNickName(deWXName(registerThirdUserRequest.getNickName()));
            registerUser.setAreaInfo(registerThirdUserRequest.getCountry() + "-" + registerThirdUserRequest.getProvince() + "-" + registerThirdUserRequest.getCity());
            registerUser.setLoginDate(new Date());
            registerUser.setLoginIp(ServletUtils.getClientIP());
            userService.insertRegister(registerUser);
            user = registerUser;
        }
        //} else {// 已有账户，关联到之前得账户即可
        //    // 查询是否用对应得token
        //    int type = 0;
        //    switch (request.getType()) {
        //        case "public":
        //            type = Constants.THIRD_LOGIN_TOKEN_TYPE_PUBLIC;
        //            break;
        //        case "routine":
        //            type = Constants.THIRD_LOGIN_TOKEN_TYPE_PROGRAM;
        //            break;
        //    }
        //
        //    UserToken userToken = userTokenService.getTokenByUserId(user.getUid(), type);
        //    if (ObjectUtil.isNotNull(userToken)) {
        //        throw new LibraryException("该手机号已被注册");
        //    }
        //    isNew = false;
        //}

        //User finalUser = user;
        //boolean finalIsNew = isNew;
        //Boolean execute = transactionTemplate.execute(e -> {
        //    if (finalIsNew) {// 新用户
        //        // 分销绑定
        //        if (userService.checkBingSpread(finalUser, registerThirdUserRequest.getSpreadPid(), "new")) {
        //            finalUser.setSpreadUid(registerThirdUserRequest.getSpreadPid());
        //            finalUser.setSpreadTime(DateUtil.nowDateTime());
        //            userService.updateSpreadCountByUid(registerThirdUserRequest.getSpreadPid(), "add");
        //        }
        //        userService.save(finalUser);
        //        // 赠送新人券
        //        giveNewPeopleCoupon(finalUser.getUid());
        //    }
        //    switch (request.getType()) {
        //        case "public":
        //            userTokenService.bind(registerThirdUserRequest.getOpenId(), Constants.THIRD_LOGIN_TOKEN_TYPE_PUBLIC, finalUser.getUid());
        //            break;
        //        case "routine":
        //            userTokenService.bind(registerThirdUserRequest.getOpenId(), Constants.THIRD_LOGIN_TOKEN_TYPE_PROGRAM, finalUser.getUid());
        //            break;
        //    }
        //    return Boolean.TRUE;
        //});
        //if (!execute) {
        //    logger.error("微信用户注册生成失败，nickName = " + registerThirdUserRequest.getNickName());
        //} else if (!isNew) {// 老用户绑定推广人
        //    if (ObjectUtil.isNotNull(registerThirdUserRequest.getSpreadPid()) && registerThirdUserRequest.getSpreadPid() > 0) {
        //        loginService.bindSpread(finalUser, registerThirdUserRequest.getSpreadPid());
        //    }
        //}

        LoginResponse loginResponse = new LoginResponse();
        XcxLoginUser loginUser = new XcxLoginUser();
        loginUser.setUserId(user.getUserId());
        loginUser.setUsername(deWXName(user.getUserName()));
        loginUser.setUserType(user.getUserType());
        LoginHelper.loginByDevice(loginUser, DeviceType.XCX);
        loginResponse.setToken(StpUtil.getTokenValue());
        loginResponse.setType("login");
        loginResponse.setUid(user.getUserId());
        loginResponse.setOpenId(registerThirdUserRequest.getOpenId());
        loginResponse.setNikeName(deWXName(user.getNickName()));
        loginResponse.setPhone(user.getPhonenumber());
        loginResponse.setAvatar(user.getWxAvatar());
        if(user.getMemberId() == null){
            user.setMemberId("1");
        }
        PGroupVo pGroupVo = iPGroupService.queryById(Long.valueOf(user.getMemberId()));
        loginResponse.setVipName(pGroupVo.getName());
        loginResponse.setUserType(user.getUserType());
        return loginResponse;
    }

    public static String deWXName(String showName){
        try{
            Pattern emoji = Pattern.compile ("[\ud83c\udc00-\ud83c\udfff]|[\ud83d\udc00-\ud83d\udfff]|[\u2600-\u27ff]",Pattern.UNICODE_CASE | Pattern . CASE_INSENSITIVE ) ;
            Matcher emojiMatcher = emoji.matcher(showName);
            if ( emojiMatcher.find())
            {
                showName = emojiMatcher.replaceAll("*");
            }
        }catch (Exception e) {

        }
        return showName;
    }

    /**
     * 微信登录
     * @return LoginResponse;
     */
    public LoginResponse weChatAuthorizeProgramLogin(String code,  RegisterThirdUserRequest request) {
        System.out.println("=======================spuid:::"+request.getSpreadPid());
        WeChatMiniAuthorizeVo response = wechatNewService.miniAuthCode(code);
        System.out.println("小程序登陆成功 = " + JSON.toJSONString(response));
        //检测是否存在
        //UserToken userToken = userTokenService.getByOpenidAndType(oauthToken.getOpenId(),  Constants.THIRD_LOGIN_TOKEN_TYPE_PUBLIC);
        SysUser userToken = userService.selectUserByWxOpenId(response.getOpenId());
        LoginResponse loginResponse = new LoginResponse();
        if (ObjectUtil.isNotNull(userToken)) {// 已存在，正常登录
            SysUser user = userService.selectUserFontById(userToken.getUserId());
            if ("1".equals(user.getStatus())) {
                throw new UserException("当前账户已禁用，请联系管理员！");
            }
            // 记录最后一次登录时间
            user.setLoginDate(DateUtil.nowDateTime());
            //Boolean execute = transactionTemplate.execute(e -> {
            //    // 分销绑定
            //    if (userService.checkBingSpread(user, spreadUid, "old")) {
            //        user.setSpreadUid(spreadUid);
            //        user.setSpreadTime(DateUtil.nowDateTime());
            //        // 处理新旧推广人数据
            //        userService.updateSpreadCountByUid(spreadUid, "add");
            //    }
            //    userService.updateById(user);
            //    return Boolean.TRUE;
            //});
            //if (!execute) {
            //    logger.error(StrUtil.format("公众号登录绑定分销关系失败，uid={},spreadUid={}", user.getUid(), spreadUid));
            //}
            //生成token
            XcxLoginUser loginUser = new XcxLoginUser();
            loginUser.setUserId(user.getUserId());
            loginUser.setUsername(user.getUserName());
            loginUser.setUserType(user.getUserType());
            LoginHelper.loginByDevice(loginUser, DeviceType.XCX);
            loginResponse.setToken(StpUtil.getTokenValue());
           //返回值
            loginResponse.setType("login");
            loginResponse.setUid(user.getUserId());
            loginResponse.setNikeName(user.getNickName());
            loginResponse.setPhone(user.getPhonenumber());
            loginResponse.setOpenId(user.getWeixinOpenid());
            loginResponse.setAvatar(user.getWxAvatar());
            PGroupVo pGroupVo = iPGroupService.queryById(Long.valueOf(user.getMemberId()));
            loginResponse.setVipName(pGroupVo.getName());
            loginResponse.setUserType(user.getUserType());
            return loginResponse;
        }
        request.setType(Constants.USER_LOGIN_TYPE_PROGRAM);
        request.setOpenId(response.getOpenId());
        String key = SecureUtil.md5(response.getOpenId());
        RedisUtils.setCacheObject(key, com.alibaba.fastjson.JSONObject.toJSONString(request), Duration.ofMinutes(2));
        loginResponse.setType("register");
        loginResponse.setKey(key);
        return loginResponse;
    }


    /**
     * 前端-微信小程序登录
     * @param code code
     * @param userRequest 授权信息
     * @return token
     */
    public Map<String, Object> xcxLogin(String code, RegisterThirdUserRequest userRequest) {
        // 通过code获取获取公众号授权信息
        // 校验 appid + appsrcret + xcxCode 调用登录凭证校验接口 获取 session_key 与 openid
        // 格式话请求参数
        String wxParam = ApiUserUtils.getWebAccess(webAccessTokenParams, appId, secret, code);
        // 请求微信路径
        String res = HttpUtils.sendGet(webAccessTokenhttps, wxParam);
        // 解析数据
        JSONObject resData = JSONUtil.parseObj(res);
        if (ObjectUtil.isEmpty(resData)) {
            throw new UserException("授权失败！");
        }
        // 校验唯一openid
        String openid = resData.getStr("openid");
        if (StringUtils.isBlank(openid)) {
            throw new UserException("授权失败，无效的openid");
        }

        SysUser user = loadUserByOpenid(openid, userRequest);
        HttpServletRequest request = ServletUtils.getRequest();
        // 此处可根据登录用户的数据不同 自行创建 loginUser
        XcxLoginUser loginUser = new XcxLoginUser();
        loginUser.setUserId(user.getUserId());
        loginUser.setUsername(user.getUserName());
        loginUser.setUserType(user.getUserType());
        loginUser.setOpenId(openid);
        // 生成token
        LoginHelper.loginByDevice(loginUser, DeviceType.XCX);
        asyncService.recordLogininfor(user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"), request);
        recordLoginInfo(user.getUserId(), user.getUserName());

        Map<String, Object> ajax = new HashMap<>();
        ajax.put("token", StpUtil.getTokenValue());
        ajax.put("userInfo", user);

        return ajax;
    }

    /**
     * 通过openid查询用户
     */
    private SysUser loadUserByOpenid(String openid, RegisterThirdUserRequest wxUserInfo) {
        SysUser user = userService.selectUserByWxOpenId(openid);
        // 第一次登录，则注册
        if (ObjectUtil.isNull(user)) {
            // 填充信息
            SysUser registerUser = new SysUser();
            registerUser.setUserName(wxUserInfo.getNickName() + CharUtil.getRandomNum(4));
            registerUser.setWeixinOpenid(openid);
            registerUser.setWxAvatar(wxUserInfo.getAvatar());
            registerUser.setPhonenumber(wxUserInfo.getPhone());
            registerUser.setStatus(PublishConstants.COMMON_NO);
            registerUser.setUserType(PublishConstants.USER_TYPE);
            registerUser.setLoginType("register");
            String gender = "2";
            //微信用户表 数据为 性别 0：未知、1：男、2：女
            //系统用户表 数据为 用户性别（0男 1女 2未知）
            if ("0".equals(wxUserInfo.getSex())) {
                gender = "2";
            }
            if ("1".equals(wxUserInfo.getSex())) {
                gender = "0";
            }
            if ("2".equals(wxUserInfo.getSex())) {
                gender = "1";
            }

            registerUser.setSex(gender);
            registerUser.setNickName(wxUserInfo.getNickName());
            registerUser.setAreaInfo(wxUserInfo.getCountry() + "-" + wxUserInfo.getProvince() + "-" + wxUserInfo.getCity());
            registerUser.setLoginDate(new Date());
            registerUser.setLoginIp(ServletUtils.getClientIP());
            userService.insertRegister(registerUser);
            user = registerUser;
        } else {
            String status = user.getStatus();
            if (PublishConstants.COMMON_YES.equals(status)) {
                throw new ServiceException("该用户已停用/审核中！");
            }

            user.setLoginType("login");
            user.setLoginDate(new Date());
            userService.updateUser(user);
        }

        return user;
    }

    public void logout(String loginName) {
        asyncService.recordLogininfor(loginName, Constants.LOGOUT, MessageUtils.message("user.logout.success"), ServletUtils.getRequest());
    }

    /**
     * 校验短信验证码
     */
    private boolean validateSmsCode(String phonenumber, String smsCode, HttpServletRequest request) {
        String code = RedisUtils.getCacheObject(Constants.CAPTCHA_CODE_KEY + phonenumber);
        if (StringUtils.isBlank(code)) {
            asyncService.recordLogininfor(phonenumber, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"), request);
            throw new CaptchaExpireException();
        }
        return code.equals(smsCode);
    }

    /**
     * 校验验证码
     *
     * @param username 用户名
     * @param code     验证码
     * @param uuid     唯一标识
     */
    public void validateCaptcha(String username, String code, String uuid, HttpServletRequest request) {
        String verifyKey = Constants.CAPTCHA_CODE_KEY + StringUtils.defaultString(uuid, "");
        String captcha = RedisUtils.getCacheObject(verifyKey);
        RedisUtils.deleteObject(verifyKey);
        if (captcha == null) {
            asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"), request);
            throw new CaptchaExpireException();
        }
        if (!code.equalsIgnoreCase(captcha)) {
            asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"), request);
            throw new CaptchaException();
        }
    }

    private SysUser loadUserByUsername(String username) {
        SysUser user = userService.selectUserByUserName(username);
        if (ObjectUtil.isNull(user)) {
            log.info("登录用户：{} 不存在.", username);
            throw new UserException("user.not.exists", username);
        } else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) {
            log.info("登录用户：{} 已被删除.", username);
            throw new UserException("user.password.delete", username);
        } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
            log.info("登录用户：{} 已被停用.", username);
            throw new UserException("user.blocked", username);
        }
        return user;
    }

    private SysUser loadUserByPhonenumber(String phonenumber) {
        SysUser user = userService.selectUserByPhonenumber(phonenumber);
        if (ObjectUtil.isNull(user)) {
            log.info("登录用户：{} 不存在.", phonenumber);
            throw new UserException("user.not.exists", phonenumber);
        } else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) {
            log.info("登录用户：{} 已被删除.", phonenumber);
            throw new UserException("user.password.delete", phonenumber);
        } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
            log.info("登录用户：{} 已被停用.", phonenumber);
            throw new UserException("user.blocked", phonenumber);
        }
        return user;
    }

    private SysUser loadUserByOpenid(String openid) {
        // 使用 openid 查询绑定用户 如未绑定用户 则根据业务自行处理 例如 创建默认用户
        // todo 自行实现 userService.selectUserByOpenid(openid);
        SysUser user = new SysUser();
        if (ObjectUtil.isNull(user)) {
            log.info("登录用户：{} 不存在.", openid);
            // todo 用户不存在 业务逻辑自行实现
        } else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) {
            log.info("登录用户：{} 已被删除.", openid);
            // todo 用户已被删除 业务逻辑自行实现
        } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
            log.info("登录用户：{} 已被停用.", openid);
            // todo 用户已被停用 业务逻辑自行实现
        }
        return user;
    }

    /**
     * 构建登录用户
     */
    private LoginUser buildLoginUser(SysUser user) {
        LoginUser loginUser = new LoginUser();
        loginUser.setUserId(user.getUserId());
        loginUser.setDeptId(user.getDeptId());
        loginUser.setUsername(user.getUserName());
        loginUser.setUserType(user.getUserType());
        loginUser.setMenuPermission(permissionService.getMenuPermission(user));
        loginUser.setRolePermission(permissionService.getRolePermission(user));
        loginUser.setDeptName(ObjectUtil.isNull(user.getDept()) ? "" : user.getDept().getDeptName());
        List<RoleDTO> roles = BeanUtil.copyToList(user.getRoles(), RoleDTO.class);
        loginUser.setRoles(roles);
        return loginUser;
    }

    /**
     * 记录登录信息
     *
     * @param userId 用户ID
     */
    public void recordLoginInfo(Long userId, String username) {
        SysUser sysUser = new SysUser();
        sysUser.setUserId(userId);
        sysUser.setLoginIp(ServletUtils.getClientIP());
        sysUser.setLoginDate(DateUtils.getNowDate());
        sysUser.setUpdateBy(username);
        userService.updateUserProfile(sysUser);
    }

    /**
     * 登录校验
     */
    private void checkLogin(LoginType loginType, String username, Supplier<Boolean> supplier) {
        HttpServletRequest request = ServletUtils.getRequest();
        String errorKey = Constants.LOGIN_ERROR + username;
        Integer errorLimitTime = Constants.LOGIN_ERROR_LIMIT_TIME;
        Integer setErrorNumber = Constants.LOGIN_ERROR_NUMBER;
        String loginFail = Constants.LOGIN_FAIL;

        // 获取用户登录错误次数(可自定义限制策略 例如: key + username + ip)
        Integer errorNumber = RedisUtils.getCacheObject(errorKey);
        // 锁定时间内登录 则踢出
        if (ObjectUtil.isNotNull(errorNumber) && errorNumber.equals(setErrorNumber)) {
            asyncService.recordLogininfor(username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), errorLimitTime), request);
            throw new UserException(loginType.getRetryLimitExceed(), errorLimitTime);
        }

        if (supplier.get()) {
            // 是否第一次
            errorNumber = ObjectUtil.isNull(errorNumber) ? 1 : errorNumber + 1;
            // 达到规定错误次数 则锁定登录
            if (errorNumber.equals(setErrorNumber)) {
                RedisUtils.setCacheObject(errorKey, errorNumber, Duration.ofMinutes(errorLimitTime));
                asyncService.recordLogininfor(username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), errorLimitTime), request);
                throw new UserException(loginType.getRetryLimitExceed(), errorLimitTime);
            } else {
                // 未达到规定错误次数 则递增
                RedisUtils.setCacheObject(errorKey, errorNumber);
                asyncService.recordLogininfor(username, loginFail, MessageUtils.message(loginType.getRetryLimitCount(), errorNumber), request);
                throw new UserException(loginType.getRetryLimitCount(), errorNumber);
            }
        }

        // 登录成功 清空错误次数
        RedisUtils.deleteObject(errorKey);
    }

    /**
     * 修改后-微信登陆
     */
    public Map<String, Object> xcxUpdateLogin(String code, RegisterThirdUserRequest userRequest) {
        // 通过code获取获取公众号授权信息
        // 校验 appid + appsrcret + xcxCode 调用登录凭证校验接口 获取 session_key 与 openid
        // 格式话请求参数
        String wxParam = ApiUserUtils.getWebAccess(webAccessTokenParams, appId, secret, code);
        // 请求微信路径
        String res = HttpUtils.sendGet(webAccessTokenhttps, wxParam);
        // 解析数据
        JSONObject resData = JSONUtil.parseObj(res);
        if (ObjectUtil.isEmpty(resData)) {
            throw new UserException("授权失败！");
        }
        // 校验唯一openid
        String openid = resData.getStr("openid");
        if (StringUtils.isBlank(openid)) {
            throw new UserException("授权失败，无效的openid");
        }
        // 鉴别登录用户
        SysUser user = loadUserByOpenid(openid, userRequest);
        // 此处可根据登录用户的数据不同 自行创建 loginUser
        XcxLoginUser loginUser = new XcxLoginUser();
        loginUser.setUserId(user.getUserId());
        loginUser.setUsername(user.getUserName());
        loginUser.setUserType(user.getUserType());
        loginUser.setOpenId(openid);
        // 生成token
        LoginHelper.loginByDevice(loginUser, DeviceType.XCX);
        // log
        HttpServletRequest request = ServletUtils.getRequest();
        asyncService.recordLogininfor(user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"), request);
        recordLoginInfo(user.getUserId(), user.getUserName());

        String key = SecureUtil.md5(openid);

        Map<String, Object> ajax = new HashMap<>();
        ajax.put("token", StpUtil.getTokenValue());
        ajax.put("userInfo", user);
        ajax.put("key", key);

        return ajax;
    }
}
